home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
ofs
/
ofsAlloc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
64KB
|
2,179 lines
/*
* ofsAlloc.c --
*
* Block and fragment allocation and truncation. This code is specific
* to 4Kbyte blocks with 1Kbyte fragments.
*
* Copyright 1987 Regents of the University of California
* All rights reserved.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/ofs/ofsAlloc.c,v 9.17 92/07/31 14:16:02 mgbaker Exp $ SPRITE (Berkeley)";
#endif not lint
#include <sprite.h>
#include <fs.h>
#include <fsutil.h>
#include <fscache.h>
#include <fslcl.h>
#include <fsNameOps.h>
#include <fsio.h>
#include <spriteTime.h>
#include <devFsOpTable.h>
#include <fsStat.h>
#include <timer.h>
#include <rpc.h>
#include <proc.h>
#include <string.h>
#include <fsdm.h>
#include <ofs.h>
#include <stdio.h>
/*
* Each domain, which is a separate piece of disk, is locked
* during allocation.
*/
#define LOCKPTR (&ofsPtr->dataBlockLock)
/*
* A table indexed by a 4 bit value is used by the allocation routine to
* quickly determine the location of 1, 2, and 3K fragments in a byte.
* The indices of the fragments start from 0. If there is no such fragment in
* the byte then a -1 is used.
*/
static int fragTable[16][OFS_NUM_FRAG_SIZES] = {
/* 0000 */ {-1, -1, -1},
/* 0001 */ {-1, -1, 0},
/* 0010 */ {3, 0, -1},
/* 0011 */ {-1, 0, -1},
/* 0100 */ {0, 2, -1},
/* 0101 */ {0, -1, -1},
/* 0110 */ {0, -1, -1},
/* 0111 */ {0, -1, -1},
/* 1000 */ {-1, -1, 1},
/* 1001 */ {-1, 1, -1},
/* 1010 */ {1, -1, -1},
/* 1011 */ {1, -1, -1},
/* 1100 */ {-1, 2, -1},
/* 1101 */ {2, -1, -1},
/* 1110 */ {3, -1, -1},
/* 1111 */ {-1, -1, -1}
};
/*
* Array to provide the ability to set and extract bits out of a bitmap byte.
*/
static unsigned char bitMasks[8] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1};
/*
* Macros to get to the 4-bit fragment masks of the two 4K blocks that are
* stored in a byte.
*/
#define UpperBlockFree(byte) (((byte) & 0xf0) == 0x00)
#define LowerBlockFree(byte) (((byte) & 0x0f) == 0x00)
#define BothBlocksFree(byte) (((byte) & 0xff) == 0x00)
#define GetUpperFragMask(byte) (((byte) >> 4) & 0x0f)
#define GetLowerFragMask(byte) ((byte) & 0x0f)
/*
* Macro to get a pointer into the bit map for a particular block.
*/
#define BlockToCylinder(ofsPtr, blockNum) \
(unsigned int) (blockNum) / (ofsPtr)->headerPtr->geometry.blocksPerCylinder
#define GetBitmapPtr(ofsPtr, blockNum) \
&((ofsPtr)->dataBlockBitmap[BlockToCylinder(ofsPtr, blockNum) * \
(ofsPtr)->bytesPerCylinder + \
((unsigned int) ((blockNum) % \
(ofsPtr)->headerPtr->geometry.blocksPerCylinder) / 2)])
#define LAST_FRAG (FS_FRAGMENTS_PER_BLOCK - 1)
#define FRAG_OFFSET_MASK (FS_FRAGMENTS_PER_BLOCK - 1)
/*
* Size of a fragment (1K).
*/
#define FRAG_SIZE (FS_BLOCK_SIZE / FS_FRAGMENTS_PER_BLOCK)
/*
* Percent of disk to keep free.
*/
int ofsPercentFree = 10;
/*
* Block allocation style (settable with FS_SET_ALLOC_GAP Fs_Command)
* CONTIGUOUS Blocks are allocated coniguously, beginning at the
* beginning of the cylinder.
* SKIP_ONE Blocks are allocated with one free block between
* each allocated block. The extra block gives the
* software time to generate a new disk request
* before the next allocated block rotates past the head.
*/
#define CONTIGUOUS 0
#define SKIP_ONE 1
int ofs_AllocGap = CONTIGUOUS;
/*
* Forward references.
*/
static void FindBlockInt _ARGS_((int hashSeed, Ofs_Domain *ofsPtr,
int nearBlock, Boolean allocate, int *blockNumPtr,
unsigned char **bitmapPtrPtr));
static ReturnStatus UpgradeFragment _ARGS_((Ofs_Domain *ofsPtr,
Fsio_FileIOHandle *handlePtr, OfsBlockIndexInfo *indexInfoPtr,
int curLastBlock, int newLastFrag, Boolean dontWriteThru,
int dontBlock, Boolean *dirtiedIndexPtr));
static ReturnStatus AllocateBlock _ARGS_((Fsio_FileIOHandle *handlePtr,
Fsdm_FileDescriptor *descPtr, OfsBlockIndexInfo *indexInfoPtr,
int newLastByte, int curLastBlock, int dontBlock,
Boolean *dirtiedIndexPtr));
static ReturnStatus FragToBlock _ARGS_((Ofs_Domain *ofsPtr,
Fsio_FileIOHandle *handlePtr, int blockNum, int dontBlock));
static void PutInBadBlockFile _ARGS_((Fsio_FileIOHandle *handlePtr,
Ofs_Domain *ofsPtr, int blockNum));
/*
*----------------------------------------------------------------------
*
* OfsBlockAllocInit() --
*
* Initialize the data structure needed for block allocation for the
* given domain on a local disk.
*
* Results:
* None.
*
* Side effects:
* Memory is allocated for the bit map, the cylinder map and the fragment
* lists. The bit map is read in and the cylinder map and fragment lists
* are initialized.
*
*----------------------------------------------------------------------
*/
ReturnStatus
OfsBlockAllocInit(ofsPtr)
register Ofs_Domain *ofsPtr; /* Domain to initialize block
* allocation for. */
{
int blocksPerCylinder;
int bitmapBytes;
register unsigned char *bitmapPtr;
register OfsCylinder *cylinderPtr;
register OfsFragment *fragPtr;
register int i;
register int j;
register int k;
int *fragOffsetPtr;
ReturnStatus status;
Sync_LockInitDynamic(&(ofsPtr->dataBlockLock), "Fs:ofsDataBlockLock");
/*
* Ensure some free disk space for disk block allocation.
*/
ofsPtr->minKFree =
(ofsPtr->headerPtr->dataBlocks * FS_FRAGMENTS_PER_BLOCK) /
ofsPercentFree;
blocksPerCylinder = ofsPtr->headerPtr->geometry.blocksPerCylinder;
/*
* Allocate the bit map.
*/
bitmapBytes = (unsigned int) (blocksPerCylinder + 1) / 2;
ofsPtr->bytesPerCylinder = bitmapBytes;
ofsPtr->dataBlockBitmap = (unsigned char *)
malloc(ofsPtr->headerPtr->bitmapBlocks * FS_BLOCK_SIZE);
/*
* Read in the bit map. The Block I/O interface is based on 1K blocks,
* but the header information is in terms of 4K blocks.
*/
status = OfsDeviceBlockIO(ofsPtr, FS_READ,
ofsPtr->headerPtr->bitmapOffset * 4,
ofsPtr->headerPtr->bitmapBlocks * 4,
(Address) ofsPtr->dataBlockBitmap);
if (status != SUCCESS) {
printf(
"OfsBlockAllocInit: Could not read data block bitmap.\n");
return(status);
}
/*
* Initialize the 3 fragment lists (1K, 2K and 3K).
*/
for (i = 0; i < OFS_NUM_FRAG_SIZES; i++) {
ofsPtr->fragLists[i] = (List_Links *) malloc(sizeof(List_Links));
List_Init(ofsPtr->fragLists[i]);
}
/*
* Allocate an array cylinder information.
*/
ofsPtr->cylinders = (OfsCylinder *)
malloc(sizeof(OfsCylinder) * ofsPtr->headerPtr->dataCylinders);
/*
* Now go through the bit map finding all of the fragments and putting
* them onto the appropriate lists. Also determine cylinder information.
*/
bitmapPtr = ofsPtr->dataBlockBitmap;
cylinderPtr = ofsPtr->cylinders;
for (i = 0; i < ofsPtr->headerPtr->dataCylinders; i++, cylinderPtr++) {
cylinderPtr->blocksFree = 0;
for (j = 0; j < bitmapBytes; j++, bitmapPtr++) {
if (UpperBlockFree(*bitmapPtr)) {
cylinderPtr->blocksFree++;
} else {
fragOffsetPtr = fragTable[GetUpperFragMask(*bitmapPtr)];
for (k = 0; k < OFS_NUM_FRAG_SIZES; k++, fragOffsetPtr++) {
if (*fragOffsetPtr != -1) {
fragPtr = (OfsFragment *) malloc(sizeof(OfsFragment));
List_Insert((List_Links *) fragPtr,
LIST_ATREAR(ofsPtr->fragLists[k]));
fragPtr->blockNum = i * blocksPerCylinder + j * 2;
}
}
}
/*
* There may be an odd number of blocks per cylinder. If so
* and are at the end of the bit map for this cylinder, then
* we can bail out now.
*/
if (j == (bitmapBytes - 1) && (blocksPerCylinder & 0x1)) {
continue;
}
if (LowerBlockFree(*bitmapPtr)) {
cylinderPtr->blocksFree++;
} else {
fragOffsetPtr = fragTable[GetLowerFragMask(*bitmapPtr)];
for (k = 0; k < OFS_NUM_FRAG_SIZES; k++, fragOffsetPtr++) {
if (*fragOffsetPtr != -1) {
fragPtr = (OfsFragment *) malloc(sizeof(OfsFragment));
List_Insert((List_Links *) fragPtr,
LIST_ATREAR(ofsPtr->fragLists[k]));
fragPtr->blockNum = i * blocksPerCylinder + j * 2 + 1;
}
}
}
}
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Ofs_BlockAllocate --
*
* Allocate disk space for the given file. This routine only allocates
* one block beginning at offset and going for numBytes. If
* offset + numBytes crosses a block boundary then a panic will occur.
*
* Results:
* None.
*
* Side effects:
* The file descriptor is modified to contain pointers to the allocated
* blocks. Also *blockAddrPtr is set to the block that was allocated.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Ofs_BlockAllocate(domainPtr, handlePtr, offset, numBytes, flags, blockAddrPtr,
newBlockPtr)
Fsdm_Domain *domainPtr; /* Domain of file. */
register Fsio_FileIOHandle *handlePtr; /* Local file handle. */
int offset; /* Offset to allocate at. */
int numBytes; /* Number of bytes to allocate. */
int flags; /* FSCACHE_DONT_BLOCK */
int *blockAddrPtr; /* Disk address of block allocated. */
Boolean *newBlockPtr; /* TRUE if there was no block allocated
* before. */
{
Ofs_Domain *ofsPtr = OFS_PTR_FROM_DOMAIN(domainPtr);
register Fsdm_FileDescriptor *descPtr;
register int blockNum;
OfsBlockIndexInfo indexInfo;
int newLastByte;
int curLastBlock;
int curLastFrag;
Boolean dirtiedIndex = FALSE;
ReturnStatus status;
descPtr = handlePtr->descPtr;
*blockAddrPtr = FSDM_NIL_INDEX;
newLastByte = offset + numBytes - 1;
blockNum = (unsigned int) offset / FS_BLOCK_SIZE;
if ((unsigned int) (newLastByte) / FS_BLOCK_SIZE != blockNum) {
panic("OfsFileAllocate: Trying to allocate more than one block\n");
}
if (descPtr->lastByte != -1) {
curLastBlock = (unsigned int) (descPtr->lastByte) / FS_BLOCK_SIZE;
} else {
curLastBlock = -1;
}
/*
* If are allocating past the current last block in the file, then
* make the last block into a full block.
*/
if (curLastBlock != -1 && curLastBlock < FSDM_NUM_DIRECT_BLOCKS &&
blockNum > curLastBlock) {
curLastFrag = (unsigned int) (descPtr->lastByte & FS_BLOCK_OFFSET_MASK)
/ FS_FRAGMENT_SIZE;
if (curLastFrag < LAST_FRAG) {
/*
* Upgrade the fragment to a full block.
*/
status = FragToBlock(ofsPtr, handlePtr, curLastBlock, flags);
if (status != SUCCESS) {
return(status);
}
}
}
/*
* Set up the indexing structure here.
*/
if (blockNum == 0) {
/*
* This is the first block of the file so there is no previous
* block.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, blockNum, &indexInfo,
OFS_ALLOC_INDIRECT_BLOCKS);
if (status != SUCCESS) {
return(status);
}
} else {
/*
* This is not the first block in the file, so determine the
* previous block and then go to the first block.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, blockNum - 1, &indexInfo,
OFS_ALLOC_INDIRECT_BLOCKS);
if (status != SUCCESS) {
return(status);
}
status = OfsGetNextIndex(handlePtr, &indexInfo, FALSE);
if (status != SUCCESS) {
OfsEndIndex(handlePtr, &indexInfo, FALSE);
return(status);
}
}
*newBlockPtr = (*indexInfo.blockAddrPtr == FSDM_NIL_INDEX);
/*
* Allocate space for the block, but only if we need to. We don't need to
* call AllocateBlock if we're not adding a new block to the end of the
* file, and we're not adding a new fragment because the block is an
* indirect block (which we don't fragment).
*/
if (!(curLastBlock == blockNum && blockNum >= FSDM_NUM_DIRECT_BLOCKS)) {
status = AllocateBlock(handlePtr, descPtr, &indexInfo, newLastByte,
curLastBlock, flags, &dirtiedIndex);
}
if (status == SUCCESS) {
*blockAddrPtr = *indexInfo.blockAddrPtr;
}
OfsEndIndex(handlePtr, &indexInfo, dirtiedIndex);
if (status != SUCCESS) {
return(status);
}
/*
* Update the size of the file. A firstByte of 0 is used in
* named pipes to note that there is data in the pipe.
* NOTE: We can almost check the stream flags FS_CONSUME here,
* except that when remote clients flush back named pipe blocks
* they clear that flag so we the server treat it like a regular
* file and don't consume or append. Hence the check against fileType.
*/
if (newLastByte > descPtr->lastByte) {
descPtr->lastByte = newLastByte;
}
if (descPtr->firstByte == -1 &&
((descPtr->fileType == FS_NAMED_PIPE) ||
(descPtr->fileType == FS_PSEUDO_DEV) ||
(descPtr->fileType == FS_XTRA_FILE))) {
descPtr->firstByte = 0;
}
descPtr->descModifyTime = Fsutil_TimeInSeconds();
descPtr->flags |= (FSDM_FD_INDEX_DIRTY|FSDM_FD_SIZE_DIRTY);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Ofs_FileTrunc --
*
* Shorten a file to length bytes. This updates the descriptor
* and may free blocks and indirect blocks from the end of the file.
*
* Results:
* Error if had problem with indirect blocks, otherwise SUCCESS.
*
* Side effects:
* Any allocated blocks after the given size are deleted.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
Ofs_FileTrunc(domainPtr, handlePtr, size, delete)
Fsdm_Domain *domainPtr;
Fsio_FileIOHandle *handlePtr; /* File to truncate. */
int size; /* Size to truncate the file to. */
Boolean delete; /* TRUE if Truncate for delete. */
{
register Ofs_Domain *ofsPtr = OFS_PTR_FROM_DOMAIN(domainPtr);
register Fsdm_FileDescriptor *descPtr;
int firstBlock;
int firstFrag = 0;
int lastBlock;
int lastFrag;
ReturnStatus status = SUCCESS;
OfsBlockIndexInfo indexInfo;
int newLastByte;
int savedLastByte;
int flags;
Boolean dirty = FALSE;
int fragsToFree;
descPtr = handlePtr->descPtr;
savedLastByte = descPtr->lastByte;
newLastByte = size - 1;
if (descPtr->lastByte <= newLastByte) {
status = SUCCESS;
goto exit;
}
/*
* Determine the first block and number of fragments in the first block.
* This is called from the pipe trunc routine if its length is zero,
* hence the check against firstByte here.
*/
if (descPtr->firstByte >= 0) {
firstBlock = (unsigned int) descPtr->firstByte / FS_BLOCK_SIZE;
} else if (newLastByte == -1) {
firstBlock = 0;
} else {
firstBlock = (unsigned int) newLastByte / FS_BLOCK_SIZE;
firstFrag = (unsigned int) (newLastByte & FS_BLOCK_OFFSET_MASK) /
FS_FRAGMENT_SIZE;
}
/*
* Determine the last block and the number of fragments in it.
*/
lastBlock = (unsigned int) descPtr->lastByte / FS_BLOCK_SIZE;
if (lastBlock >= FSDM_NUM_DIRECT_BLOCKS) {
lastFrag = LAST_FRAG;
} else {
lastFrag = (descPtr->lastByte & FS_BLOCK_OFFSET_MASK)/FS_FRAGMENT_SIZE;
}
/*
* Determine if the file is already short enough in terms of blocks.
*/
if (newLastByte != -1 && firstBlock == lastBlock &&
(lastFrag <= firstFrag || firstBlock >= FSDM_NUM_DIRECT_BLOCKS)) {
if (newLastByte < descPtr->lastByte) {
descPtr->lastByte = newLastByte;
descPtr->descModifyTime = Fsutil_TimeInSeconds();
descPtr->flags |= FSDM_FD_SIZE_DIRTY;
}
status = SUCCESS;
goto exit;
}
flags = OFS_DELETE_INDIRECT_BLOCKS;
if (newLastByte == -1) {
flags |= OFS_DELETE_EVERYTHING;
}
/*
* Loop through the blocks deleting them.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, firstBlock, &indexInfo, flags);
if (status != SUCCESS) {
printf( "Ofs_FileTrunc: Status %x setting up index\n",
status);
goto exit;
}
while (TRUE) {
if (indexInfo.blockAddrPtr == (int *) NIL ||
*indexInfo.blockAddrPtr == FSDM_NIL_INDEX) {
goto nextBlock;
}
dirty = FALSE;
if (newLastByte == -1) {
/*
* The file is being made empty.
*/
if (indexInfo.blockNum == lastBlock && lastFrag < LAST_FRAG) {
OfsFragFree(ofsPtr, lastFrag + 1,
(int) (*indexInfo.blockAddrPtr / FS_FRAGMENTS_PER_BLOCK),
*indexInfo.blockAddrPtr & FRAG_OFFSET_MASK);
descPtr->numKbytes -= lastFrag + 1;
} else {
OfsBlockFree(ofsPtr,
(int) (*indexInfo.blockAddrPtr / FS_FRAGMENTS_PER_BLOCK));
descPtr->numKbytes -= FS_FRAGMENTS_PER_BLOCK;
}
*indexInfo.blockAddrPtr = FSDM_NIL_INDEX;
} else if (indexInfo.blockNum == firstBlock) {
/*
* The first block that we truncate becomes the last block in
* the file. If we are still in direct blocks we have to
* chop this (new last block) into the right number of fragments.
*/
if (firstBlock >= FSDM_NUM_DIRECT_BLOCKS) {
goto nextBlock;
}
if (firstBlock != lastBlock) {
fragsToFree = LAST_FRAG - firstFrag;
} else {
fragsToFree = lastFrag - firstFrag;
}
if (fragsToFree > 0) {
OfsFragFree(ofsPtr, fragsToFree,
(int) (*indexInfo.blockAddrPtr / FS_FRAGMENTS_PER_BLOCK),
(*indexInfo.blockAddrPtr & FRAG_OFFSET_MASK)
+ firstFrag + 1);
descPtr->numKbytes -= fragsToFree;
}
} else if (indexInfo.blockNum >= FSDM_NUM_DIRECT_BLOCKS ||
indexInfo.blockNum < lastBlock || lastFrag == LAST_FRAG) {
/*
* This is a full block so delete it.
*/
OfsBlockFree(ofsPtr,
(int) (*indexInfo.blockAddrPtr / FS_FRAGMENTS_PER_BLOCK));
descPtr->numKbytes -= FS_FRAGMENTS_PER_BLOCK;
*indexInfo.blockAddrPtr = FSDM_NIL_INDEX;
} else {
/*
* Delete a fragment. Only get here if are on the last block in
* the file.
*/
OfsFragFree(ofsPtr, lastFrag + 1,
(int) (*indexInfo.blockAddrPtr / FS_FRAGMENTS_PER_BLOCK),
*indexInfo.blockAddrPtr & FRAG_OFFSET_MASK);
descPtr->numKbytes -= lastFrag + 1;
*indexInfo.blockAddrPtr = FSDM_NIL_INDEX;
}
dirty = TRUE;
nextBlock:
if (indexInfo.blockNum == lastBlock) {
break;
}
status = OfsGetNextIndex(handlePtr, &indexInfo, dirty);
if (status != SUCCESS) {
printf( "Ofs_FileTrunc: Could not truncate file.\n");
OfsEndIndex(handlePtr, &indexInfo, FALSE);
goto exit;
}
}
OfsEndIndex(handlePtr, &indexInfo, dirty);
descPtr->lastByte = newLastByte;
descPtr->descModifyTime = Fsutil_TimeInSeconds();
descPtr->flags |= FSDM_FD_SIZE_DIRTY;
exit:
/*
* Make sure we really deleted the file if size is zero.
*/
if (size == 0) {
register int index;
for (index=0 ; index < FSDM_NUM_DIRECT_BLOCKS ; index++) {
if (descPtr->direct[index] != FSDM_NIL_INDEX) {
printf("Ofs_FileTrunc abandoning (direct) block %d in <%d,%d> \"%s\" savedLastByte %d\n",
descPtr->direct[index],
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor,
Fsutil_HandleName((Fs_HandleHeader *)handlePtr),
savedLastByte);
descPtr->direct[index] = FSDM_NIL_INDEX;
}
}
for (index = 0 ; index <= 2 ; index++) {
if (descPtr->indirect[index] != FSDM_NIL_INDEX) {
printf("Ofs_FileTrunc abandoning (indirect) block %d in <%d,%d> \"%s\" savedLastByte %d\n",
descPtr->indirect[index],
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor,
Fsutil_HandleName((Fs_HandleHeader *)handlePtr),
savedLastByte);
descPtr->indirect[index] = FSDM_NIL_INDEX;
}
}
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* OfsWriteBackDataBlockBitmap --
*
* Write the data block bit map to disk.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
OfsWriteBackDataBlockBitmap(ofsPtr)
register Ofs_Domain *ofsPtr; /* Domain for which to write
* back the bitmap. */
{
ReturnStatus status;
LOCK_MONITOR;
status = OfsDeviceBlockIO(ofsPtr, FS_WRITE,
ofsPtr->headerPtr->bitmapOffset * 4,
ofsPtr->headerPtr->bitmapBlocks * 4,
(Address) ofsPtr->dataBlockBitmap);
if (status != SUCCESS) {
printf( "OfsWriteBackDataBlockBitmap: Could not write out data block bitmap.\n");
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* OfsWriteBackSummaryInfo --
*
* Write summary info to disk.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY ReturnStatus
OfsWriteBackSummaryInfo(ofsPtr)
register Ofs_Domain *ofsPtr; /* Domain for which to write
* back the bitmap. */
{
ReturnStatus status;
Fs_IOParam io;
Fs_IOReply reply;
LOCK_MONITOR;
bzero((Address)&io, sizeof(io));
bzero((Address)&reply, sizeof(reply));
io.buffer = (Address)ofsPtr->summaryInfoPtr;
io.length = DEV_BYTES_PER_SECTOR;
io.offset = ofsPtr->summarySector * DEV_BYTES_PER_SECTOR;
status = (*devFsOpTable[DEV_TYPE_INDEX(ofsPtr->headerPtr->device.type)].write)
(&ofsPtr->headerPtr->device, &io, &reply);
if (status != SUCCESS) {
printf("OfsWriteBackSummaryInfo: Could not write out summary info.\n");
}
if (status == GEN_NO_PERMISSION) {
printf("OfsWriteBackSummaryInfo: Disk is write-protected.\n");
status = SUCCESS;
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* SelectCylinderInt --
*
* Find a cylinder to use to allocate a block. The search starts
* with cylinderNum. If it is full then it searches neighboring
* cylinders until it finds a cylinder with a free block.
*
* Results:
* A cylinder number that contains a free block, -1 if none found.
*
* Side effects:
* The free count on the found cylinder is decremented.
*
*----------------------------------------------------------------------
*/
INTERNAL int
SelectCylinderInt(hashSeed, ofsPtr, cylinderNum)
int hashSeed; /* Seed for the hash, usually
* the file number. */
register Ofs_Domain *ofsPtr; /* Domain to select cylinder
* from. */
int cylinderNum; /* Cylinder to try first, -1
* if no preferred cylinder. */
{
register int i;
register OfsCylinder *cylinderPtr;
if (cylinderNum == -1) {
/*
* Do a random hash to find the starting point to allocate at.
*/
fs_Stats.alloc.cylHashes++;
cylinderNum = ((hashSeed * 1103515245 + 12345) & 0x7fffffff) %
ofsPtr->headerPtr->dataCylinders;
}
/*
* Search forward starting at the desired cylinder.
*/
for (i = cylinderNum, cylinderPtr = &(ofsPtr->cylinders[cylinderNum]);
i < ofsPtr->headerPtr->dataCylinders;
i++, cylinderPtr++) {
fs_Stats.alloc.cylsSearched++;
if (cylinderPtr->blocksFree > 0) {
ofsPtr->cylinders[i].blocksFree--;
return(i);
}
}
/*
* No block forward from desired cylinder so search backward.
*/
for (i = cylinderNum - 1,
cylinderPtr = &(ofsPtr->cylinders[cylinderNum - 1]);
i >= 0;
i--, cylinderPtr--) {
fs_Stats.alloc.cylsSearched++;
if (cylinderPtr->blocksFree > 0) {
ofsPtr->cylinders[i].blocksFree--;
return(i);
}
}
return(-1);
}
/*
*----------------------------------------------------------------------
*
* FindBlockInt --
*
* Search the bit map starting at the given cylinder for a free block.
*
* Results:
* A logical 4K block number for the disk where the first data block
* is block 0 and a pointer into the bitmap for the block.
*
* Side effects:
* If the allocate flag is set then the bit map is modified.
*
*----------------------------------------------------------------------
*/
INTERNAL static void
FindBlockInt(hashSeed, ofsPtr, nearBlock, allocate, blockNumPtr,
bitmapPtrPtr)
int hashSeed; /* Seed for cylinder hash. */
register Ofs_Domain *ofsPtr; /* Domain to allocate blocks in. */
int nearBlock; /* Block number where this block should
* be near. */
Boolean allocate; /* TRUE if allocating full block, FALSE
* if intend to fragment block. */
int *blockNumPtr; /* Block number that was found. */
unsigned char **bitmapPtrPtr; /* Bit map entry that corresponds to
* the block. */
{
unsigned char *bitmapPtr;
int blockNum;
int block;
int startingBlockOffset;
int blocksPerCylinder;
int mask;
int cylinderNum;
blocksPerCylinder = ofsPtr->headerPtr->geometry.blocksPerCylinder;
if (nearBlock != -1) {
cylinderNum = ((unsigned int) nearBlock) / blocksPerCylinder;
startingBlockOffset = ((unsigned int) nearBlock) % blocksPerCylinder;
} else {
cylinderNum = -1;
startingBlockOffset = -1;
}
cylinderNum = SelectCylinderInt(hashSeed, ofsPtr, cylinderNum);
if (cylinderNum == -1) {
*blockNumPtr = -1;
return;
}
if (ofs_AllocGap == 0) {
/*
* CONTIGUOUS allocation.
* This is the original code that simply starts at the beginning
* of the cylinder and stops when it finds a free block.
*/
bitmapPtr =
&(ofsPtr->dataBlockBitmap[cylinderNum * ofsPtr->bytesPerCylinder]);
blockNum = cylinderNum * blocksPerCylinder;
while (TRUE) {
fs_Stats.alloc.cylBitmapSearches++;
if (UpperBlockFree(*bitmapPtr)) {
mask = 0xf0;
break;
}
if (LowerBlockFree(*bitmapPtr)) {
mask = 0x0f;
blockNum++;
break;
}
bitmapPtr++;
blockNum += 2;
}
} else {
/*
* SKIP BLOCK allocation.
* startingBlockOffset is the offset of the last allocated block, or -1
* Set block to be ahead of the startingBlockOffset,
* and set the bitmapPtr to the corresponding spot in the bitmap.
*/
if (startingBlockOffset >= 0) {
startingBlockOffset += 1;
block = startingBlockOffset + ofs_AllocGap;
} else {
block = 0;
startingBlockOffset = blocksPerCylinder;
}
bitmapPtr =
&(ofsPtr->dataBlockBitmap[cylinderNum * ofsPtr->bytesPerCylinder
+ block / 2]);
/*
* Walk through the bitmap. We are guaranteed from SelectCylinder that
* there is a free block in this cylinder.
* Each byte in the bitmap covers two 4K blocks.
* The 'UpperBlock' covered by the byte is an even numbered block,
* and the 'LowerBlock' is odd.
*/
for ( ; block != startingBlockOffset; block++) {
fs_Stats.alloc.cylBitmapSearches++;
if (block >= blocksPerCylinder) {
/*
* Wrap back to the beginning of the cylinder
*/
block -= blocksPerCylinder;
bitmapPtr -= ofsPtr->bytesPerCylinder;
}
if ((block & 0x1) == 0 && UpperBlockFree(*bitmapPtr)) {
mask = 0xf0;
goto haveFreeBlock;
}
if ((block & 0x1) != 0 && LowerBlockFree(*bitmapPtr)) {
mask = 0x0f;
goto haveFreeBlock;
}
if (block & 0x1) {
bitmapPtr++;
}
}
UNLOCK_MONITOR;
panic("FindBlockInt: no block\n");
*blockNumPtr = -1;
ofs_AllocGap = CONTIGUOUS;
LOCK_MONITOR;
return;
haveFreeBlock:
blockNum = cylinderNum * blocksPerCylinder + block;
}
if (allocate) {
if (*bitmapPtr & mask) {
printf("bitmap = <%x>, checkMask = <%x>\n",
*bitmapPtr & 0xff, mask & 0xff);
printf("FsFindBlockInt, error in {Upper/Lower}BlockFree, failing.\n");
*blockNumPtr = -1;
return;
}
*bitmapPtr |= mask;
}
*bitmapPtrPtr = bitmapPtr;
*blockNumPtr = blockNum;
fs_Stats.alloc.blocksAllocated++;
}
/*
*----------------------------------------------------------------------
*
* OfsBlockFind --
*
* Search the bit map starting at the given cylinder for a free block.
*
* Results:
* Results from FindBlockInt.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY void
OfsBlockFind(hashSeed, ofsPtr, nearBlock, allocate, blockNumPtr, bitmapPtrPtr)
int hashSeed; /* Seed for cylinder hash. */
Ofs_Domain *ofsPtr; /* Domain to allocate blocks in . */
int nearBlock; /* Block number where this block should
* be near. */
Boolean allocate; /* TRUE if allocating full block, FALSE
* if intend to fragment block. */
int *blockNumPtr; /* Block number that was found. */
unsigned char **bitmapPtrPtr; /* Bit map entry that corresponds to
* the block. */
{
LOCK_MONITOR;
if (ofsPtr->summaryInfoPtr->numFreeKbytes - FS_FRAGMENTS_PER_BLOCK <
ofsPtr->minKFree) {
*blockNumPtr = -1;
UNLOCK_MONITOR;
return;
}
FindBlockInt(hashSeed, ofsPtr, nearBlock, allocate, blockNumPtr,
bitmapPtrPtr);
if (*blockNumPtr != -1) {
ofsPtr->summaryInfoPtr->numFreeKbytes -= FS_FRAGMENTS_PER_BLOCK;
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* OfsBlockFree --
*
* Put the given block back into the bit map.
*
* Results:
* None.
*
* Side effects:
* The bit map is modified.
*
*----------------------------------------------------------------------
*/
ENTRY void
OfsBlockFree(ofsPtr, blockNum)
register Ofs_Domain *ofsPtr; /* Handle for file to alloc blocks
* for. */
int blockNum; /* Block number to free. */
{
register unsigned char *bitmapPtr;
int cylinderNum;
register int mask;
register int checkMask;
LOCK_MONITOR;
ofsPtr->summaryInfoPtr->numFreeKbytes += FS_FRAGMENTS_PER_BLOCK;
fs_Stats.alloc.blocksFreed++;
bitmapPtr = GetBitmapPtr(ofsPtr, blockNum);
cylinderNum = (unsigned int) blockNum /
ofsPtr->headerPtr->geometry.blocksPerCylinder;
ofsPtr->cylinders[cylinderNum].blocksFree++;
if ((blockNum % ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
mask = 0xf0;
} else {
mask = 0x0f;
}
checkMask = ~mask & 0xff;
if ((*bitmapPtr & checkMask) != checkMask) {
printf("bitmap = <%x>, checkMask = <%x>\n",
*bitmapPtr & 0xff, checkMask & 0xff);
UNLOCK_MONITOR;
printf("OfsBlockFree free block %d\n", blockNum);
return;
} else {
*bitmapPtr &= mask;
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* OfsFragFind --
*
* Allocate a fragment out of the bit map. If possible the fragment
* is allocated where the last fragment was allocated.
*
* Results:
* A logical block number and offset into the block where the
* fragment begins.
*
* Side effects:
* The bit map and the fragment lists might be modified.
*
*----------------------------------------------------------------------
*/
ENTRY void
OfsFragFind(hashSeed, ofsPtr, numFrags, lastFragBlock, lastFragOffset,
lastFragSize, newFragBlockPtr, newFragOffsetPtr)
int hashSeed; /* Seed for cylinder hash. */
register Ofs_Domain *ofsPtr; /* Domain out of which to
* allocate the fragment. */
int numFrags; /* Number of fragments to get:
* 1, 2, or 3 */
int lastFragBlock; /* Block number where the last
* fragment for this file was
* allocated. */
int lastFragOffset; /* Fragment offset in the
* block. */
int lastFragSize; /* Number of fragments in the
* last fragment. */
int *newFragBlockPtr; /* Where to return new fragment
* block number. */
int *newFragOffsetPtr; /* Where to return new fragment
* offset. */
{
register OfsFragment *fragPtr;
register unsigned char *bitmapPtr = (unsigned char *) NIL;
register int *savedOffsets;
unsigned char savedBitmap;
int *fragOffsetPtr;
int fragOffset = 0;
int fragBlock;
unsigned char *tBitmapPtr;
List_Links *fragList;
int byteOffset;
int fragMask = 0;
int i;
int blockNum = -1;
int fragsToAllocate;
LOCK_MONITOR;
if (lastFragBlock == -1) {
fragsToAllocate = numFrags;
} else {
fragsToAllocate = numFrags - lastFragSize;
}
if (ofsPtr->summaryInfoPtr->numFreeKbytes - fragsToAllocate <
ofsPtr->minKFree) {
*newFragBlockPtr = -1;
UNLOCK_MONITOR;
return;
}
/*
* First try the block where the last fragment was.
*/
fs_Stats.alloc.fragsAllocated++;
if (lastFragBlock != -1 &&
lastFragOffset + numFrags <= FS_FRAGMENTS_PER_BLOCK ) {
fs_Stats.alloc.fragUpgrades++;
bitmapPtr = GetBitmapPtr(ofsPtr, lastFragBlock);
if ((lastFragBlock % ofsPtr->headerPtr->geometry.blocksPerCylinder)
& 0x1) {
byteOffset = lastFragOffset + 4;
} else {
byteOffset = lastFragOffset;
}
fragMask = 0;
blockNum = lastFragBlock;
fragOffset = lastFragOffset;
/*
* Now make sure that there are enough free fragments in the block.
*/
for (i = byteOffset + lastFragSize; i < byteOffset + numFrags; i++) {
if (*bitmapPtr & bitMasks[i]) {
blockNum = -1;
break;
}
fragMask |= bitMasks[i];
}
}
if (blockNum == -1) {
/*
* We couldn't find space in the block where the last fragment was.
* First try all fragment lists starting with the one of the
* desired size.
*/
for (i = numFrags - 1;
i < OFS_NUM_FRAG_SIZES && blockNum == -1;
i++) {
fragList = ofsPtr->fragLists[i];
while (!List_IsEmpty(fragList)) {
fragPtr = (OfsFragment *) List_First(fragList);
List_Remove((List_Links *) fragPtr);
fragBlock = fragPtr->blockNum;
free((Address) fragPtr);
/*
* Check to make sure that there really is a fragment of the
* needed size in the block. These fragment lists are hints.
*/
bitmapPtr = GetBitmapPtr(ofsPtr, fragBlock);
if ((fragBlock %
ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
fragOffset = fragTable[GetLowerFragMask(*bitmapPtr)][i];
} else {
fragOffset = fragTable[GetUpperFragMask(*bitmapPtr)][i];
}
if (fragOffset != -1) {
/*
* There is a fragment of this size so use this block.
*/
blockNum = fragBlock;
break;
} else {
fs_Stats.alloc.badFragList++;
}
}
}
if (blockNum == -1) {
fs_Stats.alloc.fullBlockFrags++;
/*
* We couldn't find a fragmented block to use so have to
* fragment a full block.
*/
FindBlockInt(hashSeed, ofsPtr, -1, FALSE, &blockNum,
&tBitmapPtr);
if (blockNum == -1) {
*newFragBlockPtr = -1;
UNLOCK_MONITOR;
return;
}
bitmapPtr = tBitmapPtr;
fragOffset = 0;
}
/*
* See if the block number corresponds to the high or low
* end of the bitmap byte. If, for example, there are an odd
* number of blocks per cylinder, an even block number may be
* odd relative to the start of the cylinder, and relative to
* the start of the bitmap for that cylinder.
*/
if ((blockNum % ofsPtr->headerPtr->geometry.blocksPerCylinder)
& 0x1) {
byteOffset = fragOffset + 4;
} else {
byteOffset = fragOffset;
}
fragMask = 0;
for (i = byteOffset; i < byteOffset + numFrags; i++) {
fragMask |= bitMasks[i];
}
ofsPtr->summaryInfoPtr->numFreeKbytes -= numFrags;
} else {
ofsPtr->summaryInfoPtr->numFreeKbytes -= numFrags - lastFragSize;
fs_Stats.alloc.fragsUpgraded++;
}
/*
* Now put the block on all appropriate fragment lists. savedOffsets
* points to the fragment offsets before we allocated the new fragment out
* of the block. fragOffsetPtr points to the fragment offset after
* we allocated the fragment out of the block.
*/
if (*bitmapPtr & fragMask) {
UNLOCK_MONITOR;
panic("Find frag bitmap error\n");
*newFragBlockPtr = -1;
return;
} else {
savedBitmap = *bitmapPtr;
*bitmapPtr |= fragMask;
}
if ((blockNum % ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
savedOffsets = fragTable[GetLowerFragMask(savedBitmap)];
fragOffsetPtr = fragTable[GetLowerFragMask(*bitmapPtr)];
} else {
savedOffsets = fragTable[GetUpperFragMask(savedBitmap)];
fragOffsetPtr = fragTable[GetUpperFragMask(*bitmapPtr)];
}
for (i = 0; i < OFS_NUM_FRAG_SIZES; i++, savedOffsets++, fragOffsetPtr++) {
if (*savedOffsets == -1 && *fragOffsetPtr != -1) {
/*
* The block was not on the fragment list of this size before we
* allocated a new fragment out of it, so put it there.
*/
fragPtr = (OfsFragment *) malloc(sizeof(OfsFragment));
List_Insert((List_Links *) fragPtr,
LIST_ATREAR(ofsPtr->fragLists[i]));
fragPtr->blockNum = blockNum;
}
}
*newFragBlockPtr = blockNum;
*newFragOffsetPtr = fragOffset;
if (fragOffset + numFrags > FS_FRAGMENTS_PER_BLOCK) {
UNLOCK_MONITOR;
panic("FsdmFragFind, fragment overrun, offset %d numFrags %d\n",
fragOffset, numFrags);
return;
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* OfsFragFree --
*
* Free the given fragment.
*
* Results:
* None.
*
* Side effects:
* The bit map and fragment lists are modified.
*
*----------------------------------------------------------------------
*/
ENTRY void
OfsFragFree(ofsPtr, numFrags, fragBlock, fragOffset)
register Ofs_Domain *ofsPtr; /* Domain out of which to allocate the
fragment. */
int numFrags; /* Number of fragments to free: 1, 2,
or 3 */
int fragBlock; /* Block number where the fragment
was allocated. */
int fragOffset; /* Fragment offset in the block. */
{
register int *fragOffsets;
register int *savedOffsets;
register unsigned char *bitmapPtr;
OfsFragment *fragPtr;
unsigned char mask;
int i;
int byteOffset;
int fragMask;
LOCK_MONITOR;
fs_Stats.alloc.fragsFreed++;
ofsPtr->summaryInfoPtr->numFreeKbytes += numFrags;
bitmapPtr = GetBitmapPtr(ofsPtr, fragBlock);
/*
* Determine whether should clear upper or lower 4 bits.
*/
if ((fragBlock % ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
byteOffset = fragOffset + 4;
savedOffsets = fragTable[GetLowerFragMask(*bitmapPtr)];
} else {
byteOffset = fragOffset;
savedOffsets = fragTable[GetUpperFragMask(*bitmapPtr)];
}
/*
* Determine the bits to unset and unset them.
*/
mask = 0;
for (i = byteOffset; i < byteOffset + numFrags; i++) {
mask |= bitMasks[i];
}
if ((*bitmapPtr & mask) != mask) {
printf("OfsFragFree: bitmap = <%x>, checkMask = <%x>\n",
*bitmapPtr & 0xff, mask & 0xff);
UNLOCK_MONITOR;
printf("OfsFragFree: block not free, block %d, numFrag %d, offset %d\n",
fragBlock, numFrags, fragOffset);
return;
} else {
*bitmapPtr &= ~mask;
}
/*
* Determine the new state of the block and put things onto the
* proper fragment lists. savedOffsets points to the array of frag
* offsets before we freed the fragment in the block and fragOffsets
* points to the frag offsets after we freed the fragment.
*/
if ((fragBlock % ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
fragMask = GetLowerFragMask(*bitmapPtr);
} else {
fragMask = GetUpperFragMask(*bitmapPtr);
}
if (fragMask == 0) {
fs_Stats.alloc.fragToBlock++;
/*
* The block has become totally free.
*/
ofsPtr->cylinders[(unsigned int) fragBlock /
ofsPtr->headerPtr->geometry.blocksPerCylinder].blocksFree++;
UNLOCK_MONITOR;
return;
}
fragOffsets = fragTable[fragMask];
for (i = 0; i < OFS_NUM_FRAG_SIZES; i++, fragOffsets++, savedOffsets++) {
if (*savedOffsets == -1 && *fragOffsets != -1) {
/*
* A fragment of this size did not exist before we freed the
* fragment but it does exist now.
*/
fragPtr = (OfsFragment *) malloc(sizeof(OfsFragment));
List_Insert((List_Links *) fragPtr,
LIST_ATREAR(ofsPtr->fragLists[i]));
fragPtr->blockNum = fragBlock;
}
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* OnlyFrag --
*
* Determine if the given fragment is the only one in the block.
*
* Results:
* TRUE if the given fragment is the only one in the block.
*
* Side effects:
* The rest of the block is marked as allocated if the given fragment
* is the only one in the block.
*
*----------------------------------------------------------------------
*/
ENTRY Boolean
OnlyFrag(ofsPtr, numFrags, fragBlock, fragOffset)
register Ofs_Domain *ofsPtr; /* Domain of fragment. */
int numFrags; /* Number of fragments to free: 1, 2,
or 3 */
int fragBlock; /* Block number where the fragment
was allocated. */
int fragOffset; /* Fragment offset in the block. */
{
register unsigned char *bitmapPtr;
unsigned char mask;
int i;
int byteOffset;
int blockMask;
LOCK_MONITOR;
bitmapPtr = GetBitmapPtr(ofsPtr, fragBlock);
/*
* Determine whether should access upper or lower 4 bits.
*/
if ((fragBlock % ofsPtr->headerPtr->geometry.blocksPerCylinder) & 0x1) {
byteOffset = fragOffset + 4;
blockMask = 0x0f;
} else {
byteOffset = fragOffset;
blockMask = 0xf0;
}
/*
* Determine which bits are set for this fragment.
*/
mask = 0;
for (i = byteOffset; i < byteOffset + numFrags; i++) {
mask |= bitMasks[i];
}
if ((*bitmapPtr & mask) != mask) {
printf("bitmap = <%x>, checkMask = <%x>\n",
*bitmapPtr & 0xff, mask & 0xff);
UNLOCK_MONITOR;
panic("OnlyFrag: Frag block corrupted.\n");
return(FALSE);
}
if (((*bitmapPtr & ~mask) & blockMask) != 0) {
/*
* There is another fragment in this block so we can't put the block
* into the bad block file yet.
*/
UNLOCK_MONITOR;
return(FALSE);
}
/*
* We were the only fragment in this block. Mark the rest of the block
* as allocated because our caller is going to put this block into the
* bad block file.
*/
*bitmapPtr |= blockMask;
ofsPtr->summaryInfoPtr->numFreeKbytes -=
FS_FRAGMENTS_PER_BLOCK - numFrags;
UNLOCK_MONITOR;
return(TRUE);
}
/*
*----------------------------------------------------------------------
*
* UpgradeFragment --
*
* Take a fragment and make it of sufficient size to fit the new
* fragment size.
*
* Results:
* SUCCESS, FS_NO_DISK_SPACE, or FS_WOULD_BLOCK (if cache is full).
*
* Side effects:
* *indexInfoPtr may be modified along with *indexInfoPtr->blockAddrPtr.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
UpgradeFragment(ofsPtr, handlePtr, indexInfoPtr, curLastBlock, newLastFrag,
dontWriteThru, dontBlock, dirtiedIndexPtr)
Ofs_Domain *ofsPtr; /* Domain of file. */
Fsio_FileIOHandle *handlePtr; /* File to allocate blocks
* for. */
register OfsBlockIndexInfo *indexInfoPtr; /* Index info structure. */
int curLastBlock; /* The current last block. */
int newLastFrag; /* New last fragment for this
* file. Fragments are numbered
* from 0. */
Boolean dontWriteThru; /* TRUE => make sure that the
* cache block that contains
* upgraded block isn't forced
* through to disk. */
int dontBlock; /* FSCACHE_DONT_BLOCK */
Boolean *dirtiedIndexPtr; /* TRUE if modified the block
* pointer in the file index
* structure. */
{
register Fsdm_FileDescriptor *descPtr;
register int blockAddr;
int curLastFrag; /* Current last fragment for
* this file. */
int curFragBlock; /* Current disk block used for
* last block in file. */
int curFragOffset; /* Offset in the block where
* the fragment begins. */
int newFragBlock; /* New disk block used for last
block in file. */
int newFragOffset; /* Offset in new block where
* the fragment begins. */
unsigned char *bitmapPtr;
Fscache_Block *fragCacheBlockPtr;
Boolean found;
ReturnStatus status = SUCCESS;
int flags;
descPtr = handlePtr->descPtr;
blockAddr = *(indexInfoPtr->blockAddrPtr);
curLastFrag = (unsigned int) (descPtr->lastByte & FS_BLOCK_OFFSET_MASK) /
FS_FRAGMENT_SIZE;
if (curLastBlock >= FSDM_NUM_DIRECT_BLOCKS || curLastFrag == LAST_FRAG ||
curLastFrag >= newLastFrag) {
/*
* There is already enough space so return.
*/
goto exit;
}
curFragBlock = (unsigned int) blockAddr / FS_FRAGMENTS_PER_BLOCK;
curFragOffset = blockAddr & FRAG_OFFSET_MASK;
if (newLastFrag < LAST_FRAG) {
/*
* Have to allocate a larger fragment. Note that fragments are
* numbered from zero so that the fragment number + 1 is equal to the
* number of fragments in the block.
*/
OfsFragFind(handlePtr->hdr.fileID.minor, ofsPtr, newLastFrag + 1,
curFragBlock, curFragOffset, curLastFrag + 1,
&newFragBlock, &newFragOffset);
if (newFragBlock == -1) {
printf("UpgradeFragment: OfsFragFind failed: no space on %s.\n",
ofsPtr->domainPtr->domainPrefix); /* DEBUG */
status = FS_NO_DISK_SPACE;
goto exit;
}
if (curFragBlock == newFragBlock && curFragOffset == newFragOffset) {
/*
* Were able to extend the old fragment so return.
*/
descPtr->numKbytes += newLastFrag - curLastFrag;
goto exit;
}
} else {
/*
* Allocate a full block.
*/
OfsBlockFind(handlePtr->hdr.fileID.minor, ofsPtr,
indexInfoPtr->lastDiskBlock,
TRUE, &newFragBlock, &bitmapPtr);
if (newFragBlock == -1) {
printf("UpgradeFragment: OfsBlockFind failed: no space on %s.\n",
ofsPtr->domainPtr->domainPrefix); /* DEBUG */
status = FS_NO_DISK_SPACE;
goto exit;
} else if (newFragBlock == 0 && handlePtr->hdr.fileID.minor != 2) {
printf("UpgradeFragment: tried to allocate block 0 to non-root file #%d\n",
handlePtr->hdr.fileID.minor);
status = FAILURE;
goto exit;
}
newFragOffset = 0;
}
/*
* Copy over the old fragment into the new larger one. This
* is done by fetching the block into the cache, switching the value in
* the file descriptor and marking the block dirty.
*/
Fscache_FetchBlock(&handlePtr->cacheInfo,
curLastBlock, FSCACHE_DATA_BLOCK | dontBlock,
&fragCacheBlockPtr, &found);
if (fragCacheBlockPtr == (Fscache_Block *)NIL) {
status = FS_WOULD_BLOCK;
goto exit;
}
fs_Stats.blockCache.fragAccesses++;
if (!found) {
status = OfsDeviceBlockIO(ofsPtr, FS_READ,
blockAddr +
ofsPtr->headerPtr->dataOffset * FS_FRAGMENTS_PER_BLOCK,
curLastFrag + 1, fragCacheBlockPtr->blockAddr);
if (status != SUCCESS) {
Fscache_UnlockBlock(fragCacheBlockPtr, 0, -1, 0, 0);
OfsFragFree(ofsPtr, newLastFrag + 1,
newFragBlock, newFragOffset);
goto exit;
}
fs_Stats.blockCache.fragZeroFills++;
/*
* Zero fill the rest of the block.
*/
bzero(fragCacheBlockPtr->blockAddr +
(curLastFrag + 1) * FS_FRAGMENT_SIZE,
FS_BLOCK_SIZE - (curLastFrag + 1) * FS_FRAGMENT_SIZE);
} else {
fs_Stats.blockCache.fragHits++;
if (fragCacheBlockPtr->flags & FSCACHE_READ_AHEAD_BLOCK) {
fs_Stats.blockCache.readAheadHits++;
}
}
/*
* Commit the change in the fragments location.
* 1 - unlock the cache block, specifying the new location.
* This step blocks if I/O is in progress on the block.
* After any I/O (such as a writeback) completes, then
* the block will be put on the dirty list with the new address.
* 2 - update the file descriptors indexing information to point to
* the new block.
* 3 - free up the old fragment.
*
* (As a historical note, steps 1 & 2 used to be reversed. Files
* were ending up with the wrong trailing fragment occasionally.)
*/
if (dontWriteThru) {
flags = FSCACHE_CLEAR_READ_AHEAD | FSCACHE_DONT_WRITE_THRU;
} else {
flags = FSCACHE_CLEAR_READ_AHEAD;
}
blockAddr = newFragBlock * FS_FRAGMENTS_PER_BLOCK + newFragOffset;
Fscache_UnlockBlock(fragCacheBlockPtr, (unsigned) Fsutil_TimeInSeconds(),
blockAddr,
(newLastFrag + 1) * FS_FRAGMENT_SIZE, flags);
*(indexInfoPtr->blockAddrPtr) = blockAddr;
descPtr->numKbytes += newLastFrag - curLastFrag;
descPtr->flags |= FSDM_FD_SIZE_DIRTY;
*dirtiedIndexPtr = TRUE;
OfsFragFree(ofsPtr, curLastFrag + 1, curFragBlock, curFragOffset);
exit:
return(status);
}
/*
*----------------------------------------------------------------------
*
* AllocateBlock --
*
* Allocate a block for the file.
*
* Results:
* FS_NO_DISK_SPACE if could not allocate block. Otherwise,
* returns SUCCESS.
*
* Side effects:
* Also *indexInfoPtr may be modified along with *indexInfoPtr->blockAddr.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
AllocateBlock(handlePtr, descPtr, indexInfoPtr, newLastByte, curLastBlock,
dontBlock, dirtiedIndexPtr)
Fsio_FileIOHandle *handlePtr; /* File to allocate block for.*/
register Fsdm_FileDescriptor *descPtr; /* Pointer to the file desc. */
register OfsBlockIndexInfo *indexInfoPtr; /* Index info structure. */
int newLastByte; /* The new last byte in the
* file. */
int curLastBlock; /* The last block in the file
* before started allocating. */
int dontBlock; /* FSCACHE_DONT_BLOCK */
Boolean *dirtiedIndexPtr;/* TRUE if a new block was
* allocated. */
{
register int blockAddr;
register Ofs_Domain *ofsPtr;
unsigned char *bitmapPtr;
int newFragIndex; /* {0, 1, 2, 3} */
int blockNum; /* Disk block that is
* allocated. */
int newFragOffset; /* Offset in disk block where
* fragment begins. */
ReturnStatus status = SUCCESS;
ofsPtr = indexInfoPtr->ofsPtr;
blockAddr = *(indexInfoPtr->blockAddrPtr);
if (blockAddr == 0 && handlePtr->hdr.fileID.minor != 2) {
/*
* The zero'th block belongs to the root directory which is
* created by the makeFilesystem program.
*/
printf("AllocateBlock: non-root file <%d,%d> with block 0\n",
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor);
return(FAILURE);
}
if (indexInfoPtr->blockNum >= FSDM_NUM_DIRECT_BLOCKS ||
indexInfoPtr->blockNum < curLastBlock) {
newFragIndex = LAST_FRAG;
} else {
newFragIndex =
(unsigned int) (newLastByte & FS_BLOCK_OFFSET_MASK) / FS_FRAGMENT_SIZE;
}
if (descPtr->lastByte == -1 || indexInfoPtr->blockNum != curLastBlock) {
/*
* Empty file or we are allocating a block that is before the
* last block in the file.
*/
if (newFragIndex < LAST_FRAG) {
/*
* Fragment the last block.
*/
OfsFragFind(handlePtr->hdr.fileID.minor, ofsPtr,
newFragIndex + 1, -1, -1, -1,
&blockNum, &newFragOffset);
if (blockNum != -1) {
*(indexInfoPtr->blockAddrPtr) =
blockNum * FS_FRAGMENTS_PER_BLOCK + newFragOffset;
*dirtiedIndexPtr = TRUE;
descPtr->numKbytes += newFragIndex + 1;
} else {
printf("AllocateBlock: OfsFragFind failed: no space on %s.\n",
ofsPtr->domainPtr->domainPrefix); /* DEBUG */
status = FS_NO_DISK_SPACE;
}
} else {
/*
* Allocate a full block if one isn't there already.
*/
if (blockAddr == FSDM_NIL_INDEX) {
OfsBlockFind(handlePtr->hdr.fileID.minor, ofsPtr,
indexInfoPtr->lastDiskBlock,
TRUE, &blockNum, &bitmapPtr);
if (blockNum == -1) {
printf("AllocateBlock: OfsBlockFind failed: no space on %s.\n",
ofsPtr->domainPtr->domainPrefix); /* DEBUG */
status = FS_NO_DISK_SPACE;
} else if (blockNum == 0 && handlePtr->hdr.fileID.minor != 2) {
/*
* The zero'th block belongs to the root directory which is
* created by the makeFilesystem program.
*/
printf("AllocateBlock: non-root file <%d,%d> wants block 0\n",
handlePtr->hdr.fileID.major,
handlePtr->hdr.fileID.minor);
status = FAILURE;
} else {
*(indexInfoPtr->blockAddrPtr) =
blockNum * FS_FRAGMENTS_PER_BLOCK;
descPtr->numKbytes += FS_FRAGMENTS_PER_BLOCK;
*dirtiedIndexPtr = TRUE;
}
}
}
} else {
/*
* Are allocating on top of the last block so make sure that the
* last fragment is large enough.
*/
status = UpgradeFragment(ofsPtr, handlePtr, indexInfoPtr,
curLastBlock, newFragIndex, TRUE,
dontBlock, dirtiedIndexPtr);
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* FragToBlock --
*
* Upgrade the given fragment to a block.
*
* Results:
* SUCCESS, FS_NO_DISK_SPACE, FS_WOULD_BLOCK.
*
* Side effects:
* The size in the file descriptor is updated so that it is on a block
* boundary.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
FragToBlock(ofsPtr, handlePtr, blockNum, dontBlock)
Ofs_Domain *ofsPtr;
register Fsio_FileIOHandle *handlePtr;
int blockNum;
int dontBlock; /* FSCACHE_DONT_BLOCK */
{
register Fsdm_FileDescriptor *descPtr;
OfsBlockIndexInfo indexInfo;
ReturnStatus status;
Boolean dirtiedIndex;
/*
* Set up the indexing structure.
*/
descPtr = handlePtr->descPtr;
if (blockNum == 0) {
/*
* This is the first block of the file so there is no previous
* block.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, blockNum, &indexInfo,
OFS_ALLOC_INDIRECT_BLOCKS);
if (status != SUCCESS) {
return(status);
}
} else {
/*
* This is not the first block in the file, so determine the
* previous block and then go to the first block.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, blockNum - 1, &indexInfo,
OFS_ALLOC_INDIRECT_BLOCKS);
if (status != SUCCESS) {
return(status);
}
status = OfsGetNextIndex(handlePtr, &indexInfo, FALSE);
if (status != SUCCESS) {
OfsEndIndex(handlePtr, &indexInfo, FALSE);
return(status);
}
}
/*
* Now upgrade to a full block.
*/
status = UpgradeFragment(ofsPtr, handlePtr, &indexInfo, blockNum, LAST_FRAG,
FALSE, dontBlock, &dirtiedIndex);
if (status == SUCCESS) {
descPtr->lastByte = blockNum * FS_BLOCK_SIZE + FS_BLOCK_SIZE - 1;
descPtr->descModifyTime = Fsutil_TimeInSeconds();
descPtr->flags |= FSDM_FD_SIZE_DIRTY;
}
OfsEndIndex(handlePtr, &indexInfo, dirtiedIndex);
return(status);
}
/*
*----------------------------------------------------------------------
*
* Ofs_DomainInfo --
*
* Return info about the given domain.
*
* Results:
* Error if can't get to the domain.
*
* Side effects:
* The domain info struct is filled in.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Ofs_DomainInfo(domainPtr, domainInfoPtr)
Fsdm_Domain *domainPtr;
Fs_DomainInfo *domainInfoPtr;
{
Ofs_Domain *ofsPtr = OFS_PTR_FROM_DOMAIN(domainPtr);
domainInfoPtr->maxKbytes =
ofsPtr->headerPtr->dataBlocks * FS_FRAGMENTS_PER_BLOCK;
domainInfoPtr->freeKbytes = ofsPtr->summaryInfoPtr->numFreeKbytes;
domainInfoPtr->maxFileDesc = ofsPtr->headerPtr->numFileDesc;
domainInfoPtr->freeFileDesc = ofsPtr->summaryInfoPtr->numFreeFileDesc;
domainInfoPtr->blockSize = FS_BLOCK_SIZE;
domainInfoPtr->optSize = FS_BLOCK_SIZE;
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Ofs_ReallocBlock --
*
* Allocate a new block on disk to replace the given block. This is
* intended to be used by the cache when it can't write out a block
* because of a disk error.
*
* Results:
* None
*
* Side effects:
* The descriptor or indirect blocks are modified to point to the newly
* allocated block.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
void
Ofs_ReallocBlock(data, callInfoPtr)
ClientData data; /* Block to move */
Proc_CallInfo *callInfoPtr; /* Not used. */
{
Fscache_Block *blockPtr = (Fscache_Block *) data;
Fscache_FileInfo *cacheInfoPtr = blockPtr->cacheInfoPtr;
Fsio_FileIOHandle *handlePtr = (Fsio_FileIOHandle *) cacheInfoPtr->hdrPtr;
int virtBlockNum, physBlockNum;
OfsBlockIndexInfo indexInfo;
Fsdm_Domain *domainPtr;
int newBlockNum = -1;
Boolean dirtiedIndex = FALSE;
Boolean setupIndex = FALSE;
unsigned char *bitmapPtr;
ReturnStatus status;
Fsdm_FileDescriptor *descPtr;
Ofs_Domain *ofsPtr;
virtBlockNum = blockPtr->blockNum;
physBlockNum = blockPtr->diskBlock;
if (handlePtr->hdr.fileID.minor == 0) {
/*
* This is a descriptor block.
*/
printf(
"OfsBlockRealloc: Bad descriptor block. Domain=%d block=%d\n",
handlePtr->hdr.fileID.major, physBlockNum);
goto error1;
}
domainPtr = Fsdm_DomainFetch(handlePtr->hdr.fileID.major, FALSE);
if (domainPtr == (Fsdm_Domain *)NIL) {
goto error;
}
ofsPtr = OFS_PTR_FROM_DOMAIN(domainPtr);
Fsutil_HandleLock((Fs_HandleHeader *)handlePtr);
descPtr = handlePtr->descPtr;
if (virtBlockNum >= 0) {
int bytesInBlock;
/*
* A normal data block.
*/
status = OfsGetFirstIndex(ofsPtr, handlePtr, virtBlockNum, &indexInfo, 0);
if (status != SUCCESS) {
printf(
"OfsBlockRealloc: Setup index (1) failed status <%x>\n", status);
goto error;
}
setupIndex = TRUE;
if (*indexInfo.blockAddrPtr != physBlockNum) {
panic("OfsBlockRealloc: Bad physical block num.\n");
}
bytesInBlock = descPtr->lastByte - virtBlockNum * FS_BLOCK_SIZE + 1;
if (bytesInBlock > FS_FRAGMENT_SIZE * (FS_FRAGMENTS_PER_BLOCK - 1) ||
virtBlockNum >= FSDM_NUM_DIRECT_BLOCKS) {
/*
* Have a full block.
*/
OfsBlockFind(handlePtr->hdr.fileID.minor, ofsPtr,
physBlockNum / FS_FRAGMENTS_PER_BLOCK, TRUE,
&newBlockNum, &bitmapPtr);
if (newBlockNum == -1) {
printf( "FsdmBlockRealloc: No disk space (1)\n");
goto error;
}
newBlockNum *= FS_FRAGMENTS_PER_BLOCK;
*indexInfo.blockAddrPtr = newBlockNum;
dirtiedIndex = TRUE;
descPtr->flags |= FSDM_FD_INDEX_DIRTY;
PutInBadBlockFile(handlePtr, ofsPtr, physBlockNum);
} else {
int newFragOffset;
int numFrags;
/*
* Have a fragment.
*/
numFrags = (bytesInBlock - 1) / FS_FRAGMENT_SIZE + 1;
OfsFragFind(handlePtr->hdr.fileID.minor, ofsPtr, numFrags,
-1, -1, -1, &newBlockNum, &newFragOffset);
if (newBlockNum == -1) {
printf( "FsdmBlockRealloc: No disk space (2)\n");
goto error;
}
newBlockNum = newBlockNum * FS_FRAGMENTS_PER_BLOCK + newFragOffset;
*indexInfo.blockAddrPtr = newBlockNum;
dirtiedIndex = TRUE;
descPtr->flags |= FSDM_FD_INDEX_DIRTY;
if (OnlyFrag(ofsPtr, numFrags,
physBlockNum / FS_FRAGMENTS_PER_BLOCK,
physBlockNum & FRAG_OFFSET_MASK)) {
PutInBadBlockFile(handlePtr, ofsPtr,
physBlockNum & ~FRAG_OFFSET_MASK);
} else {
/*
* The fragment is in a block with other valid fragments.
* We do nothing and just leave the fragment allocated
* in the bitmap but unreferenced by any file.
* This means checkFS should verify that an unreferenced
* fragment is readable before marking it free.
*/
printf( "Leaving bad frag #%d unreferenced\n",
physBlockNum);
}
}
} else {
Fscache_Block *blockPtr = (Fscache_Block *)NIL;
int *blockAddrPtr;
physBlockNum = -physBlockNum;
if (virtBlockNum == -1) {
/*
* This is the first indirect block.
*/
blockAddrPtr = &descPtr->indirect[0];
} else if (virtBlockNum == -2) {
/*
* Second indirect block.
*/
blockAddrPtr = &descPtr->indirect[1];
} else if (descPtr->indirect[1] == FSDM_NIL_INDEX) {
panic("OfsBlockRealloc: Can't find indirect block\n");
blockAddrPtr = (int *) NIL;
} else {
Boolean found;
/*
* Read in the doubly indirect block so that we can get to the
* indirect block that we want.
*/
fs_Stats.blockCache.indBlockAccesses++;
Fscache_FetchBlock(&handlePtr->cacheInfo, -2,
FSCACHE_IND_BLOCK, &blockPtr, &found);
if (!found) {
status = OfsDeviceBlockIO(ofsPtr, FS_READ,
descPtr->indirect[1], FS_FRAGMENTS_PER_BLOCK,
blockPtr->blockAddr);
if (status != SUCCESS) {
printf(
"OfsBlockRealloc: Could not read doubly indirect block, status <%x>\n",
status);
Fscache_UnlockBlock(blockPtr, 0, 0, 0, FSCACHE_DELETE_BLOCK);
goto error;
} else {
fs_Stats.gen.physBytesRead += FS_BLOCK_SIZE;
}
} else {
fs_Stats.blockCache.indBlockHits++;
}
blockAddrPtr = (int *)blockPtr->blockAddr + (-virtBlockNum - 3);
}
if (*blockAddrPtr != physBlockNum) {
panic("OfsBlockRealloc: Bad phys addr for indirect block (2)\n");
}
/*
* Allocate a new indirect block.
*/
OfsBlockFind(handlePtr->hdr.fileID.minor, ofsPtr, -1, TRUE,
&newBlockNum, &bitmapPtr);
if (newBlockNum == -1) {
printf( "FsdmBlockRealloc: No disk space (3)\n");
goto error;
}
newBlockNum = (newBlockNum + ofsPtr->headerPtr->dataOffset) *
FS_FRAGMENTS_PER_BLOCK;
*blockAddrPtr = newBlockNum;
if (blockPtr == (Fscache_Block *)NIL) {
descPtr->flags |= FSDM_FD_INDEX_DIRTY;
} else {
Fscache_UnlockBlock(blockPtr, (unsigned int)Fsutil_TimeInSeconds(),
-(descPtr->indirect[1]), FS_BLOCK_SIZE, 0);
}
PutInBadBlockFile(handlePtr, ofsPtr,
physBlockNum - FS_FRAGMENTS_PER_BLOCK *
ofsPtr->headerPtr->dataOffset);
newBlockNum = -newBlockNum;
}
error:
if (setupIndex) {
OfsEndIndex(handlePtr, &indexInfo, dirtiedIndex);
}
Fsdm_DomainRelease(handlePtr->hdr.fileID.major);
Fsutil_HandleUnlock((Fs_HandleHeader *)handlePtr);
error1:
FscacheFinishRealloc(blockPtr, newBlockNum);
return;
}
/*
*----------------------------------------------------------------------
*
* PutInBadBlockFile --
*
* Put the given physical block into the bad block file.
*
* Results:
* None.
*
* Side effects:
* Block appended to the bad block file.
*
*----------------------------------------------------------------------
*/
static void
PutInBadBlockFile(handlePtr, ofsPtr, blockNum)
Fsio_FileIOHandle *handlePtr; /* File which owned bad block. */
Ofs_Domain *ofsPtr; /* Pointer to domain. */
int blockNum; /* Block number to put in bad block file. */
{
Fs_FileID fileID;
Fsio_FileIOHandle *badBlockHandlePtr;
Fsdm_FileDescriptor *descPtr;
OfsBlockIndexInfo indexInfo;
ReturnStatus status;
int lastBlock;
fileID.serverID = rpc_SpriteID;
fileID.type = FSIO_LCL_FILE_STREAM;
fileID.major = handlePtr->hdr.fileID.major;
fileID.minor = OFS_BAD_BLOCK_FILE_NUMBER;
badBlockHandlePtr = (Fsio_FileIOHandle *)Fsutil_HandleFetch(&fileID);
if (badBlockHandlePtr == (Fsio_FileIOHandle *)NIL) {
/*
* Have to make a new handle since we don't have one for this domain
* in memory.
*/
status = Fsio_LocalFileHandleInit(&fileID, "BadBlockFile",
(Fsdm_FileDescriptor *) NIL, FALSE,
&badBlockHandlePtr);
if (status != SUCCESS) {
printf("PutInBadBlockFile: error %x reading descriptor\n", status);
return;
}
}
descPtr = badBlockHandlePtr->descPtr;
if (descPtr->lastByte != -1) {
lastBlock = descPtr->lastByte / FS_BLOCK_SIZE;
} else {
lastBlock = -1;
}
status = OfsGetFirstIndex(ofsPtr, handlePtr, lastBlock + 1, &indexInfo,
OFS_ALLOC_INDIRECT_BLOCKS);
if (status != SUCCESS) {
printf( "PutInBadBlockFile: Could not fetch index\n");
} else {
*indexInfo.blockAddrPtr = blockNum;
descPtr->lastByte += FS_BLOCK_SIZE;
descPtr->flags |= (FSDM_FD_INDEX_DIRTY|FSDM_FD_SIZE_DIRTY);
descPtr->numKbytes += FS_FRAGMENTS_PER_BLOCK;
OfsEndIndex(handlePtr, &indexInfo, TRUE);
}
Fsutil_HandleUnlock((Fs_HandleHeader *)badBlockHandlePtr);
}